package com.stone.starter.core.script.tool;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义类加载器，将类的字节码二进制内容加载为class放入到内存中
 *
 * @author Mr_wenpan@163.com 2021/9/7 10:56 上午
 */
public class MemoryClassLoader extends URLClassLoader {

    /**
     * 编译结果放在Map<String, byte[]>中，Key是类名，对应的byte[]是class的二进制内容
     * 为什么一个.java文件编译后不是一个byte[]呢？
     * 因为一个.java的源文件编译后可能有多个.class文件！只要包含了静态类、匿名类等，编译出的class肯定多于一个
     */
    private final Map<String, byte[]> classBytes;

    public MemoryClassLoader(Map<String, byte[]> classBytes) {
        super(new URL[0], MemoryClassLoader.class.getClassLoader());
        this.classBytes = new ConcurrentHashMap<>();
        this.classBytes.putAll(classBytes);
    }

    /**
     * 覆写findClass方法，在类加载器加载字节码文件的时候会调用该方法，
     * 该方法会从我们已经编译好的classBytes缓存中根据类名查找类并返回该类的Class对象
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 获取内存中的字节码
        byte[] buf = classBytes.get(name);
        if (buf == null) {
            return super.findClass(name);
        }
        // 如果没有字节码内容，则移除
        classBytes.remove(name);
        // 字节码以二进制的形式交给defineClass方法加载
        return defineClass(name, buf, 0, buf.length);
    }

}
